深入java注解开发 + spring注解开发

您所在的位置:网站首页 java spring 注解 深入java注解开发 + spring注解开发

深入java注解开发 + spring注解开发

#深入java注解开发 + spring注解开发| 来源: 网络整理| 查看: 265

文章目录 java注解: 介绍注解成员元注解` @Retention`: 依据保留级别`@Target `: 指定注解的作用对象`@Inherited`:注解的继承性`@Native``@Repeatable``@Documented` java注解实战编译期修改语法树使用略解JCTree工具包使用示例相关链接 注解和反射基于spring 相关结合spring boot 扩展点组合注解造轮子: API工具 注解和接口 总结疑问

基于java1.8

java注解: 介绍

关于『注解』和『XML』两种不同的配置模式,争论了好多年了,各有各的优劣,注解可以提供更大的便捷性,易于维护修改,但耦合度高,而 XML 相对于注解则是相反的。

追求低耦合就要抛弃高效率,追求效率必然会遇到耦合。

所有的注解都实现了java.lang.annotation.Annotation这个接口。一个注解如果没有对应的处理器的话,其实就当于注释。注解处理器一般来说有两种实现: 编译期扫描(代码生成、增强)、运行时反射。此外还可结合“静态代码分析工具|框架”使用。

注解成员

根据 JLS中章节9.6.1 的说明,Java中注解成员的类型必须是如下几类:

基本数据类型(boolean, byte, char, short, int, long, float, double等);不包括其包装类型String;Class;枚举;其他的注解;以上类型的数组; 元注解

元注解是指注解的注解。包括 @Retention、@Target、 @Document、 @Inherited四种。java8引入了@Native和@Repeatable

@Retention: 依据保留级别

可以分为三种: 源码级、class文件级、运行时级。三种级别的定义在java.lang.annotation.RetentionPolicy枚举量里。

当注解未定义Retention值时,默认值是CLASS。

public enum RetentionPolicy { SOURCE, // 源码 CLASS, // class RUNTIME // 运行时 }

不同的保留级别标识着对应注解能存在到的层次:

常见如@Override、@SuppressWarnings等以及lombok的 @Data、@Getter、@SneakyThrows都是源码级别的注解,java文件编译成class文件后,这些注解就不在了。lombok的@NonNull是class级别的注解,在class文件中依旧存在。常见的spring注解: @Component、@Controller等式运行时注解,若使用反射的方式处理注解,则必须要RUNTIME型。 @Target: 指定注解的作用对象

作用对象以枚举的形式列在java.lang.annotation.ElementType中。当注解未指定Target值时,此注解可以使用任何元素之上。

public enum ElementType { /* 类、接口、注解、枚举*/ TYPE, /** 表示该注解可以用于字段,包括枚举实例 */ FIELD, /** 表示该注解可以用于方法 */ METHOD, /** 表示该注解可以用于参数声明 */ PARAMETER, /** 表示该注解可以用于构造函数声明 */ CONSTRUCTOR, /** 表示该注解可以用于局部变量声明 */ LOCAL_VARIABLE, /** 表示该注解可以用于注解声明 */ ANNOTATION_TYPE, /** 表示该注解可以用于包声明 */ PACKAGE, /** java8新加入,表示该注解可以用于类型参数声明: 重在声明*/ TYPE_PARAMETER, /** java8新加入,表示该注解可以用于类型使用声明: 重在使用*/ TYPE_USE }

TYPE_USE和TYPE_PARAMETER要么结合编译器处理器使用,要么结合静态分析工具使用。反射型的处理器处理不了一些太复杂的情况(如下所示)。

TYPE_USE表示该注解能使用在使用类型的任意语句中。只要是类型名称,都可以进行注解。

//new MyClass aClass = new @myAnnoUse MyClass(); //cast Object o = "test"; String str = (@myAnnoUse String) o; //范型 public String getClasses(List list){ return null; }

TYPE_PARAMETER

// 标注在范型上(但TYPE_USE也可以实现) class testClass {} @Inherited:注解的继承性

结合反射使用。

@Documented @Retention(RetentionPolicy.RUNTIME) // 保留到运行时,因此适用于继承 @Target(ElementType.ANNOTATION_TYPE) // 只针对注解 public @interface Inherited { }

可以让注解被继承,但这并不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解。

注: @Inherited只会对类继承类的场景起作用。类实现接口、接口继承接口的情况不会起作用。

以下举个例子阐述,类实现接口、接口继承接口的情况不会起作用。

注解

@Target(value = {ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) @Inherited @interface DESC { String value() default ""; }

类继承、接口实现

@DESC("super") class WithAno {} class WithOutAno extends WithAno {} @Test public void test() { //父子类继承 会继承 Arrays.stream(WithOutAno.class.getAnnotations()).forEach(System.out::println); } @Native

使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。

@Repeatable

在java8之前,注解是不可以重复注在同一个地方的(方法、类、成员字段等),如果要注在同一个字段,需要新建一个新的注解,如下例子所示:

@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Game { String type(); } @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @interface Games { Game[] value(); // 要多次注的注解构成数组 } // 对于java8之前,必须要这样使用才可以 @Games(value={ @Game("1"), @Game("2"), }) class Test{} // 在java8中可以注解重复 @Game("1") @Game("2") class Test{}

其实本质上就是个语法糖。以下以@Game和@Games注解举例说明。

反编译后会发现其实这个新特性就是将多个@Game注解编译成@Games,所以这里会有一个坑: 需要兼容1个或多个的场景

如果该注解在某个地方只注上一次,反编译后的代码是注上@Game的(只有一个@Game注解的话,不会自动编译成@Games)

如果被注上多次,则反编译后的代码里是被@Games注了,而不是被@Game注。

@Documented

被修饰的注解会生成到javadoc中,可以使用javadoc命令生成文档。

java注解实战 编译期修改语法树

Java5中提供了apt工具来进行编译期的注解处理。apt是命令行工具,与之配套的是一套描述“程序在编译时刻的静态结构”的API:Mirror API(com.sun.mirror.*)

在JDK6中,将注解处理器这一功能进行了规范化,形成了java.annotation.processing的API包,Mirror API则进行封装,形成javax.lang.model包。注解处理器的开发进行了简化,不再单独使用apt工具。

注解处理器的作用原理可以如下图所示,在生成语法树后和生成字节码之间,修改语法树进而实现代码增强、代码生成。lombok就是基于此的。自定义的注解器相当于“编译器插件”的存在。

image-20210314162924512

使用略解

继承 AbstractProcessor 添加@AutoService(Processor.class)注解,,用来生成META-INF/services/javax.annotation.processing.Processor文件的

重载:init、getSupportedAnnotationTypes、getSupportedSourceVersion方法

init(ProcessingEnvironment processingEnvironment)做一些初始化操作,可搞可不搞

Set getSupportedAnnotationTypes()指定这个注解处理器给哪个注解使用,将注解的合法全程放在Set里。例如Msg.class.getCanonicalName。

getSupportedSourceVersion()指定java版本

实现process(Set



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3